package org.example.codeservce.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.novelweb.tool.upload.file.FileInfo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import org.example.codeservce.comm.Q;
import org.example.codeservce.dao.OrderDao;
import org.example.codeservce.entity.FileCode;
import org.example.codeservce.entity.Order;
import org.example.codeservce.entity.to.OrderTO;
import org.example.codeservce.service.FileCodeService;
import org.example.codeservce.service.OrderService;
import org.example.codeservce.service.PayConfigService;
import org.example.codeservce.status.PayStatus;
import org.example.codeservce.util.AlipayUtil;
import org.example.codeservce.util.CodeAnalysisToolsUtil;
import org.example.codeservce.util.TreeDataUtil;
import org.example.codeservce.util.ZipUtilQ;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import static org.example.codeservce.config.FileConfig.FILEPATH;

/**
 * (Order)表服务实现类
 *
 * @author LiQing
 * @since 2024-05-15 11:48:12
 */
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, Order> implements OrderService {

    @Resource
    private ThreadPoolExecutor poolExecutor;
    @Resource
    private CodeAnalysisToolsUtil codeAnalysisToolsUtil;

    @Resource
    private FileCodeService fileCodeService;

    @Resource
    private PayConfigService payConfigService;

    @Resource
    private AlipayUtil alipayUtil;

    @SneakyThrows
    @Override
    public Q uploadCodeFile(String orderNo, String fileName, String filePath, Integer type) {

        // 校验传入参数是否为空
        if (orderNo == null || fileName == null || type == null) {
            throw new IllegalArgumentException("缺少重要参数!");
        }
        // 校验订单
        Order order = getOrder(orderNo);
        if (order == null) {
            throw new IllegalArgumentException("非法订单号!");
        }

        // 判断订单状态是否允许提交源码分析任务
        if (!isOrderValidForAnalysis(order)) {
            throw new IllegalStateException("订单已经提交源码分析任务!");
        }

        try {
            // 保存文件信息到数据库
            FileCode newFileCode = saveFileCode(fileName);
            // 将文件与订单关联
            bindFileToOrder(order, newFileCode);
            // 异步执行源码分析任务
            executeAsyncCodeAnalysis(new File(filePath), type, newFileCode, order);
            return Q.ok().message(fileName + "，源码分析中...");
        } catch (IllegalStateException e) {
            // 文件保存失败或者订单状态不合法时抛出异常
            throw new MultipartException("保存文件失败", e);
        }
    }

    /**
     * 构建订单并且
     *
     * @return
     */
    @Override
    public Q builderPay() {
        Order order = getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getLgId, StpUtil.getLoginId())
                .eq(Order::getStatus, 0)
        );
        if (order == null) {
            order = new Order() {{
                setOrderNo(UUID.randomUUID().toString().replaceAll("-", ""));
                setLgId(Integer.parseInt(StpUtil.getLoginId().toString()));
                setStatus(0);
                setPrice(payConfigService.getById(1).getPrice());
            }};
        }
        String codeStr = alipayUtil.builderPay("Java源码分析平台", order.getPrice().toString()
                , "www.xiaoqing.work", order.getOrderNo());
        byte[] pngArr = QrCodeUtil.generatePng(codeStr,
                300, 300);
        String base64 = Base64Utils.encodeToString(pngArr);
        boolean b = order.getId() == null ? save(order) : updateById(order);
        return Q.ok().q("payImage", base64).q("orderNo", order.getOrderNo());
    }

    @Override
    public Q info(String orderNo) {
        Order order = getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getOrderNo, orderNo));
        if (order == null) {
            return Q.no(450, "订单不存在");
        }
        if (order.getLgId() != StpUtil.getLoginIdAsInt()) {
            return Q.no(450, "只能自己解析的源码可以进行查看哦！");
        }
        Integer orderFcId = order.getFcId();
        FileCode codeServiceById = fileCodeService.getById(orderFcId);
        String readUtf8String = FileUtil.readUtf8String(FILEPATH + orderNo + "/classPromptVo.json");
        codeServiceById.setClassPromptVo(readUtf8String);
        return TreeDataUtil.todoTree(codeServiceById);
    }

    @Override
    public void callback(BigDecimal parsedBigDecimal, String outTradeNo) {
        update(new LambdaUpdateWrapper<Order>()
                .eq(Order::getOrderNo, outTradeNo)
                .set(Order::getStatus, 1)
        );
    }

    @Override
    public List<OrderTO> byUserGetOrder() {
        List<Order> list = list(new LambdaQueryWrapper<Order>()
                .eq(Order::getLgId, StpUtil.getLoginId()));
        if (CollectionUtils.isNotEmpty(list)) {
            return list.stream().map(order -> {
                OrderTO orderTO = new OrderTO();
                BeanUtils.copyProperties(order, orderTO);
                if (!Objects.isNull(order.getFcId())){
                    FileCode fileCode = fileCodeService.getById(order.getFcId());
                    Optional.ofNullable(fileCode)
                            .ifPresent(op -> {
                                orderTO.setFileId(op.getId());
                                orderTO.setFileStatus(op.getStatus());
                            });
                }

                return orderTO;
            }).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }


    // 获取订单信息
    private Order getOrder(String orderNo) {
        return getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNo, orderNo));
    }

    // 判断订单是否允许进行源码分析任务
    private boolean isOrderValidForAnalysis(Order order) {
        return order.getStatus() == PayStatus.Paid.ordinal() && order.getFcId() == null;
    }

    // 生成文件名
    private String generateFileName(MultipartFile file) {
        return UUID.randomUUID().toString().replaceAll("-", "") + file.getOriginalFilename();
    }

    // 保存文件到本地磁盘
    private String saveFile(MultipartFile file, String fileName) throws IOException {
        String filePath = ZipUtilQ.ZIP_FILE_PATH + fileName;
        File dest = new File(filePath);
        file.transferTo(dest);
        return filePath;
    }

    // 将文件信息保存到数据库
    private FileCode saveFileCode(String fileName) {
        Integer loginId = (Integer) StpUtil.getLoginIdAsInt();
        FileCode fileCode = new FileCode();
        fileCode.setLgId(loginId);
        fileCode.setFileName(fileName);
        fileCodeService.save(fileCode);
        return fileCode;
    }

    // 将文件与订单关联
    private void bindFileToOrder(Order order, FileCode newFileCode) {
        order.setFcId(newFileCode.getId());
        updateById(order);
    }

    // 异步执行源码分析任务
    private void executeAsyncCodeAnalysis(File fileName, Integer type, FileCode newFileCode, Order order) {
        poolExecutor.execute(() ->
        {
            codeAnalysisToolsUtil.codeAnalysisTools(fileName, type, newFileCode, order);
        });
    }

}

